home *** CD-ROM | disk | FTP | other *** search
/ SuperHack / SuperHack CD.bin / CODING / DELPHI / EZDSL200.ZIP / EZDSL.DOC < prev    next >
Encoding:
Text File  |  1996-03-13  |  96.9 KB  |  2,428 lines

  1. ======================================================================
  2.                                 EZDSL
  3.                             version 2.00
  4.  
  5.           Easy classical data structures for Delphi 1 and 2
  6.  
  7.               Copyright (c) 1993,1996 Julian M. Bucknall
  8. ======================================================================
  9.  
  10.  
  11. Introduction
  12. ----------------------------------------------------------------------
  13.  
  14. The EZDSL units provide an OOP interface for classical data structures
  15. for Delphi: stacks, queues, lists, binary trees and so forth. For
  16. programmers migrating from BP7 a TCollection replacement is also
  17. provided.
  18.  
  19. My objective in writing these units was to provide myself with a set
  20. of well-encapsulated classes that would manage the tedium of using
  21. these types of data structures, leaving me to concentrate on coding
  22. my latest program. Having used Borland's TCollection from Turbo
  23. Vision and OWL a great deal, I also wanted to mimic as far as I could
  24. the functionality and naming conventions contained within that
  25. object, without necessarily restricting myself by doing so.
  26.  
  27. This library was rewritten from my EZSTRUCS library that I first
  28. released in August 1994. It was not a simple port; I was determined to
  29. use some of the whizz new syntax of Delphi: exceptions, virtual
  30. constructors, properties and so on. The first version of EZDSL
  31. appeared in 1995, this later version was ported to 32 bits for the
  32. Delphi 2.0 compiler; of course making sure that it could still be
  33. compiled with Delphi 1.0 in 16 bits.
  34.  
  35. Within these notes I shall not spend too much time describing how the
  36. data structures work and what they're for; any data structures book
  37. will be able to provide that kind of information easily. The three I
  38. have made a great deal of use of (and that I recommend heartily) are
  39.  
  40.   Algorithms, by Robert Sedgewick; Addison-Wesley
  41.   Data Structures, Algorithms and Performance, by Derek Wood;
  42.     Addison-Wesley
  43.   Practical Data Structures in C++, by Bryan Flamig; Wiley
  44.  
  45. Also, the three volumes of The Art of Computer Programming by Knuth
  46. belong on every serious programmer's shelf, although they are
  47. starting to look their age as far as the programming examples go.
  48.  
  49. Also these units require that you have a fairly good grounding in
  50. using pointers, objects and classes in Delphi, including typecasting.
  51. If you need to, please review your Delphi documentation on how to use
  52. them.
  53.  
  54.  
  55. Container classes and Data objects
  56. ----------------------------------------------------------------------
  57.  
  58. The EZDSLxxx units define a set of data object containers. These
  59. are kinds of containers into which you throw a bunch of data 'things'
  60. and retrieve them at a later stage. The data 'things' can be
  61. anything:  integers, strings, records, object instances, or whatever;
  62. we'll use the generic term data object. The different kinds of
  63. containers each have different characteristics, some will keep the
  64. data objects sorted in some order, some will give you fast serial
  65. access times, some will just give you them back in the same order you
  66. put them in.  Some will allow you to navigate through the structure
  67. (and will provide a classic iterator method), some do not.
  68.  
  69. The containers can be separated into two types: those that own their
  70. data objects, and those that do not. Being a data owner means that a
  71. container will destroy its data objects when it is emptied or itself
  72. destroyed. This distinction means that you could create two skip
  73. lists for example, one a data owner and the other not, each with a
  74. different sort sequence. You could then insert the same set of data
  75. objects into both and know that when you call the Empty method for
  76. both lists, the data objects will get destroyed only once.
  77.  
  78. As you look at the code for the containers and at this documentation,
  79. you'll see that the hierarchy tree is fairly flat (or narrow if you
  80. look at it vertically):
  81.  
  82.        TAbstractContainer                 (EZDSLBSE.PAS)
  83.          +--TStack                        (EZDSLSTK.PAS)
  84.          +--TQueue                        (EZDSLQUE.PAS)
  85.          |    +--TDeque                   (EZDSLQUE.PAS)
  86.          +--TPriorityQueue                (EZDSLPQU.PAS)
  87.          +--TLinkList                     (EZDSLLST.PAS)
  88.          +--TDList                        (EZDSLDBL.PAS)
  89.          +--TSkipList                     (EZDSLSKL.PAS)
  90.          +--TBinTree                      (EZDSLBTR.PAS)
  91.          |    +--TBinSearchTree           (EZDSLBTR.PAS)
  92.          |         +--TrbSearchTree       (EZDSLBTR.PAS)
  93.          +--TEZCollection                 (EZDSLCOL.PAS)
  94.               +--TEZSortedCollection      (EZDSLCOL.PAS)
  95.                    +--TEZStringCollection (EZDSLCOL.PAS)
  96.                    +--TEZStrZCollection   (EZDSLCOL.PAS)
  97.  
  98. There is a single ancestor class that provides the important common
  99. methods and fields (allocating/freeing a node, whether a container is
  100. empty) and the base of the virtual methods hierarchy (Compare,
  101. DisposeData, et al). Most of the other classes are descended directly
  102. from this, with only a few further descendants. This is contrary to
  103. lots of other container hierarchies where the author has endeavoured
  104. to arrange things so that everything descends from one another all the
  105. way back to a doubly linked list or something. I felt that this type
  106. of scheme was too restricting: if I found a better implementation of a
  107. list (and I think it can be done better with TEZCollections for
  108. example) I don't have to worry too much about breaking up my hierarchy
  109. (so long as I interface the same methods). The other thing is that
  110. deriving a stack from a linked list for example means that not only do
  111. you get the Push and Pop methods but you also get a whole slew of
  112. linked list methods appearing as well (Prev, Next, Insert, Delete or
  113. whatever).
  114.  
  115. However I have tried to be consistent in my naming convention so that
  116. for example, there is a method called Next for a TLinkList, TDList and
  117. TSkipList but it works in different ways for each (and has a different
  118. calling interface) - the Next method is not virtual.
  119.  
  120. The EZDSL library has been written assuming that the data objects are
  121. pointers to something. Like TCollections from BP7, this makes it easy
  122. to have the containers have lots of different kinds of objects and to
  123. be able to deal with them properly.  However, there is nothing to stop
  124. you using pointers to strings or even plain longints instead; just
  125. typecast to a pointer when required (and typecast back again when the
  126. object is retreived from the container!). For example when using a
  127. stack you could use integers as follows:
  128.  
  129.   var
  130.     MyStack : TStack;
  131.   begin
  132.     MyStack.Init;
  133.     with MyStack do
  134.       begin
  135.         Push(pointer(19));
  136.         Push(pointer(57));
  137.         writeln(longint(Pop)); {outputs: 57}
  138.         writeln(longint(Pop)); {outputs: 19}
  139.       end;
  140.     MyStack.Done;
  141.   end;
  142.  
  143. Or you could write a container that would wrap up this slight
  144. awkwardness and hide it from you. See the example programs EXINTQUE
  145. and EXSTRSTK.
  146.  
  147.  
  148. Data Objects and Polymorphism
  149. ----------------------------------------------------------------------
  150.  
  151. The first version of the EZSTRUCS unit (on which this library is
  152. based) had virtual methods called Compare, DisposeData and DupData.
  153. However, I found that often the ONLY methods I was overriding were...
  154. Compare, DisposeData and DupData! The data container worked fine 'out
  155. of the box', it was such a pain to override yet another class just to
  156. alter one of these methods.
  157.  
  158. I had two solutions to my perceived problem: make the data objects
  159. derive from an ancestor object class or make the methods function
  160. pointers.  The first was out (I find it awkward), but the second was
  161. just what I wanted. In fact Delphi itself is rife with this kind of
  162. delegation model.
  163.  
  164. So, the data object related methods in the containers are in fact
  165. procedure and function pointers held as properties in the container.
  166. You set them when you initialise a new container, and they will be
  167. used whenever two data objects need to be compared, or a data object
  168. needs to be disposed of or you need to duplicate a data object.
  169.  
  170. To set them you first create your new container object by calling the
  171. Create constructor. This sets the internal procedure and function
  172. pointers of the object to 'do nothing' routines. Then you set the
  173. actual routines you want to use by modifying the required properties:
  174. Compare, DisposeData and DupData.  EZDSL provides the ones used most
  175. often in EZDSLSUP.PAS:  comparison between two longints, between two
  176. strings (short strings in Delphi 2.0), disposing and duplicating the
  177. same.  Please note that it doesn't make any sense to alter these
  178. routines when the container is not empty and so setting these
  179. properties in that case do nothing. In particular you cannot suddenly
  180. change the order of data objects in a sorted container by changing the
  181. Compare property that the container uses.
  182.  
  183. The Compare, DisposeData and DupData routines that you write *must*
  184. be global, declared 'far' in Delphi 1.0 and declared using the normal
  185. Pascal fastcall calling convention (viz 'register') in Delphi 2.0. In
  186. other words
  187.  
  188.    (a) they cannot be nested routines;
  189.    (b) in Delphi 1.0 they cannot be declared implicitly or explicitly
  190.        as near routines; and
  191.    (c) in Delphi 2.0 cannot be declared 'cdecl' or 'stdcall'.
  192.  
  193. They must also follow the routine prototypes TCompareFunc,
  194. TDisposeDataProc and TDupDataFunc respectively.
  195.  
  196. To facilitate cloning containers with the most flexibility, you can
  197. clone a container and use a different Compare function than the
  198. container you are cloning from (useful for maintaining trees or lists
  199. with their data objects in two different orders).
  200.  
  201.  
  202. Sorted Containers, Compare and Duplicate Data Objects
  203. ----------------------------------------------------------------------
  204.  
  205. A number of the containers in EZDSL maintain their data objects in
  206. some kind of ordered sequence. The sequence that they are maintained
  207. in is defined by the container's Compare property.
  208.  
  209. Some containers are automatically and always 'sorted': skip lists
  210. and binary search trees are two examples. Some are never sorted:
  211. stacks and queues for example. Some containers can be used sorted or
  212. unsorted: examples are linked lists or double linked lists.
  213.  
  214. The Compare property is a function that you write (or is one of the
  215. supplied functions). The routine must compare two data objects and
  216. return a negative number (eg -1) if the first data object is 'less'
  217. than the second, zero if they are equal, and a positive number (eg 1)
  218. if the first is greater than the second.  As described above you set
  219. a container's Compare property when the container is empty.  The
  220. Compare function is used only by sorted containers or by unsorted
  221. containers that implement a Search method.  Unsorted containers that
  222. do not have a Search method do not use the Compare property, and so in
  223. these cases it does not have to be set.
  224.  
  225. Once you accept that certain containers maintain data objects in an
  226. ordered sequence, you have to consider the problem of duplicate data
  227. objects. Two data objects are defined as duplicate if the container's
  228. Compare function returns zero when called with them as parameters.
  229. Well, EZDSL enforces a strict rule: sorted containers *cannot*
  230. contain duplicate data objects. All sorted containers will raise an
  231. exception (with string code edsInsertDup) if you attempt to insert a
  232. data object that compares equal to an already inserted data object.
  233.  
  234. You might have decided that this is a somewhat harsh restriction.
  235. Well, yes and no. Some containers just cannot be used with duplicate
  236. data objects (the skip list is the main example; if you try to, some
  237. wierd effects will occur).  Some containers will internally reorganise
  238. themselves, and will destroy any arrival sequence that duplicate data
  239. objects could have been inserted with (the red-black binary search
  240. tree is the main example here). Rather than document that some
  241. containers will accept duplicate items and some won't and if they did
  242. what kind of wierd effects could happen on deletion and insertion, I
  243. decided to restrict all sorted containers in the same manner: they
  244. will not accept duplicate data objects.
  245.  
  246. What to do? Suppose you had the kind of use for a skip list that
  247. absolutely had to accept duplicate data objects? Firstly I would ask
  248. you to redesign your data object so that duplicates could not occur
  249. and secondly recode your Compare function so that maybe another field
  250. of the data object could be used in the comparison to remove the
  251. ambiguity.
  252.  
  253. If all fails, one solution could be to declare a sequence long integer
  254. in your container descendant class. Set this to zero in your
  255. constructor. Make sure that your data objects have a sequence field,
  256. and that your Compare function checks this after your main comparison.
  257. Override the Insert method of the container to increment the
  258. container's sequence field and set the data object's sequence field to
  259. it, and then call the inherited insert method.  This algorithm is
  260. shown in the example program EXINSDUP.
  261.  
  262. As always with rules, there is one exception to the 'no duplicates'
  263. rule:  the priority queue. This container will accept duplicate data
  264. objects.  However duplicate data objects will *not* be popped off in
  265. arrival sequence or, reverse arrival sequence, or any other
  266. deterministic sequence; as far as the priority queue is concerned
  267. duplicate data objects are exactly that, it cannot distinguish
  268. between them in any way. Again if this matters to you, you'll HAVE to
  269. redefine your data object and Compare function to remove the anomaly.
  270.  
  271.  
  272. Nodes and Node Stores
  273. ----------------------------------------------------------------------
  274.  
  275. We all know that these classical data structures are generally
  276. implemented with nodes. For a doubly linked list a node is usually
  277. represented by a structure of the form:
  278.  
  279.   PMyNode = ^TMyNode;
  280.   TMyNode = record
  281.     Next : PMyNode;     {Link to next node in the chain}
  282.     Prev : PMyNode;     {Link to previous node in the chain}
  283.     Data : SomeRecord;
  284.   end;
  285.  
  286. When writing the unit I wanted to get away from the dependance of
  287. knowing about this typical node structure: I wanted to insert, push,
  288. examine, delete or pop data objects without having to be continually
  289. reminded of the underlying algorithm. Also, I didn't want to have to
  290. descend my data objects from a TNode object - I would be forced to
  291. always use TNode descendants and couldn't use PStrings or something
  292. along those lines. Also, by divorcing myself from knowing about
  293. (worrying about) the node structure, I was able to make numerous
  294. economies in data storage and speed.
  295.  
  296. However, some of these data structures cry out for being able to
  297. navigate through the structure. The navigation is performed by the
  298. container class' methods. Sometimes where you are in the
  299. structure is stored internally in the container object (an internal
  300. cursor), sometimes you have explicit 'cursors' to help you move around
  301. the container (external cursors). All containers have methods to move
  302. these internal and external cursors around the object. Of course
  303. stacks and queues and similar structures do not, you can only
  304. reference the topmost object (they are inherently non-navigable).
  305.  
  306. Also because, the node structure is hidden I was able to implement a
  307. node suballocator scheme (called a TNodeStore; see the source) to
  308. help me and the containers manage blocks of nodes, rather than just
  309. one at a time.  Because the nodes are all the same size for a given
  310. container type (with two exceptions: TSkipList and TEZCollection), we
  311. can take advantage of this and speed up allocations and deallocations
  312. of nodes, compared with using the heap. So if you look at the
  313. TNodeStore code you'll see things like node reuse, several containers
  314. using the same node store and so on.
  315.  
  316.  
  317. Iterators
  318. ----------------------------------------------------------------------
  319.  
  320. For the navigable containers (ie those containers where a cursor is
  321. defined) an iterator method is defined. This is called Iterate, and
  322. takes over from the two iterators called ForEach and FirstThat from
  323. olden days; the Iterate method does double duty as will be seen.
  324.  
  325. Each iterator takes three parameters: an Action routine, a direction
  326. flag and a ExtraData pointer. The ExtraData pointer is simply to
  327. enable you to pass any kind of record structure to the Action
  328. routine: it negates the need for global variables or for a nested
  329. Action routine (c.f.  Borland's TCollection in BP7). The direction
  330. flag (called Backwards) enables you to iterate through the container
  331. from either one of two directions: forwards or backwards.
  332.  
  333. The Action routine for a ForEach call has to be of the form:
  334.  
  335.   TIteratorProc = function (C : TAbstractContainer;
  336.                             aData : pointer;
  337.                             ExtraData : pointer) : boolean;
  338.  
  339. where C will be the container itself, aData is the current data
  340. object and ExtraData is the same extra data pointer you passed to
  341. ForEach. The function must return True to cause the Iterate routine
  342. to continue iterating or False to cause the Iterate routine to stop
  343. (and return the data object that cause the Action routine to return
  344. False).  Remember that your Action procedure CANNOT be a nested
  345. routine (in Delphi 1.0 it must be declared 'far'; in Delphi 2.0 it
  346. must be declared as 'register' (ie the normal fastcall declaration)).
  347.  
  348. A quick example: suppose your data objects had an integer field called
  349. Value and you wanted to find the sum of all the Value fields in a
  350. list. Your Action procedure would look like:
  351.  
  352.   function SumValues(C : TAbstractContainer;
  353.                      aData : pointer;
  354.                      ExtraData : pointer) : boolean; far;
  355.     var
  356.       {ExtraData is a pointer to a longint: so typecast it}
  357.       Sum :  ^longint absolute ExtraData;
  358.     begin
  359.       inc(Sum^, PMyObject(aData).Value);
  360.       Result := true;
  361.     end;
  362.  
  363. and you would call Iterate to do the summation as follows:
  364.  
  365.   var
  366.     TotalValue : longint;
  367.     MyList : TLinkList;
  368.   begin
  369.     {...}
  370.     TotalValue := 0; {clear the total}
  371.     MyList.Iterate(SumValues, false, @TotalValue);
  372.     {...}
  373.   end.
  374.  
  375. Notice that the SumValues procedure is explicitly declared 'far' in
  376. Delphi 1.0 (in Delphi 2.0 this modifier is ignored). I have found this
  377. method to be by far (!) the easiest method of ensuring that the Delphi
  378. 1.0 compiler will compile my source code without relying on the
  379. current value of the $F compiler define.
  380.  
  381.  
  382. Debugging and Errors and Exceptions
  383. ----------------------------------------------------------------------
  384.  
  385. When I started writing this unit I had several goals, but there were
  386. two which seemed incompatible: it had to be fast and it had to have
  387. lots of checks built in to trap any errors that might occur.
  388.  
  389. This second goal is further complicated because there are broadly two
  390. types of error that could occur: an error due to a programming
  391. mistake and errors due to some run-time problem. This is necessarily
  392. a wishy-washy definition, but generally the run-time problems would
  393. be things like running out of memory whilst adding a data object, and
  394. the programming mistakes would be things like trying to pop a data
  395. object from an empty stack. Another way to view this would be to
  396. define programming mistakes as being those things which would apply
  397. to every machine, whereas the run-time problems would vary from user
  398. to user and from machine to machine. Normal testing should identify
  399. programming mistakes, whereas the other type of error are exceptions
  400. to the norm.
  401.  
  402. I thought about how I might trap programming mistakes and decided to
  403. use assertion checks as in the C language. An assertion is a simple
  404. debugging routine that checks whether something is true.  If this is
  405. the case the program continues. If it is false, the program is
  406. terminated immediately by writing out a string explaining why (we
  407. shall however be raising an exception).  In C this is accomplished by
  408. the Assert macro.  Compile your C program in one way during testing
  409. and the Assert macro expands to this kind of check; compile it in
  410. another way (for your production app) and the Assert macro 'expands'
  411. to a null statement.  As you see, during testing you have the full
  412. set of checks to aid in debugging (slow but safe), and once you have
  413. fully tested the application you can turn them all off to obtain the
  414. full speed.
  415.  
  416. Unfortunately in Pascal these kind of preprocessor macros are not
  417. available. I worked round the problem by having a compiler define
  418. called DEBUG which firstly automatically activates the $D+ and $L+
  419. compiler options, and secondly causes a lot of methods to have a call
  420. to Assert at the start of the routine to check that various entry
  421. conditions are met.
  422.  
  423. An example should make this clear. The TStack object has a method
  424. called Pop to remove the topmost data object from the stack. If the
  425. stack is empty, I count calling Pop as a programming mistake: you
  426. really should check for the stack being empty in your program prior
  427. to calling Pop. Of course Pop could have an if statement within it
  428. that did this check for you, but in the *majority* of cases the stack
  429. won't be empty when Pop is called and in the *majority* of cases when
  430. you use Pop, you'll have some kind of loop in your program which is
  431. continually checking whether the stack is empty or not anyway. In my
  432. mind having a check for an empty stack within Pop is safe but slow.
  433. So, instead, Pop has a call to an Assert procedure at the start
  434. (activated by the DEBUG compiler define) that checks to see whether
  435. the stack is empty. Here is the code for Pop:
  436.  
  437.       function TStack.Pop : pointer;
  438.         var
  439.           Node : PNode;
  440.         begin
  441.           {$IFDEF DEBUG}
  442.           Assert(not IsEmpty, ascEmptyPop);
  443.           {$ENDIF}
  444.           Node := Head^.Link;
  445.           Head^.Link := Node^.Link;
  446.           Pop := Node^.Data;
  447.           acDisposeNode(Node);
  448.         end;
  449.  
  450. As you see, if DEBUG is set the Assert procedure checks whether the
  451. stack is empty first, if not it executes the code that pops the data
  452. object off the stack. If the stack is empty an EEZAssertionError
  453. exception is raised (the constant ascEmptyPop is a string code for a
  454. stringtable resource).  If DEBUG is not set the code runs at full
  455. speed.
  456.  
  457. In the method descriptions below, I will show when an assertion check
  458. (activated with the DEBUG define) has been built into the method's
  459. code. You may assume that if the DEBUG define is off and the
  460. condition that the assertion check test for occurs, very bad things
  461. will happen to your program. These could be as 'benign' as a memory
  462. leak, or as dreadful as a memory overwrite. It is up to you to
  463. thoroughly test your program with the DEBUG define set, before you
  464. turn it off.
  465.  
  466. The other type of error (that which occurs infrequently and is due to
  467. some 'at the limit' problem) will cause an EEZContainerError
  468. exception to be raised. The strings that the exception class uses are
  469. defined in a stringtable resource (EZDSLCTS.RES, string codes in
  470. EZDSLCTS.PAS), so that you may alter them at will (eg translate them
  471. into another language).
  472.  
  473. You can rest assured that when exceptions are raised, resources will
  474. be conserved via try..finally blocks. If you also have EZSTRUCS you
  475. might find it amazing how easy try..finally blocks make your code
  476. easier to understand compared with the 'old' method involving flags
  477. and checks for errors all over the place.
  478.  
  479.  
  480. Compiler Defines
  481. ----------------------------------------------------------------------
  482.  
  483. At the beginning of every EZDSL source code file are the following
  484. lines:
  485.  
  486.     {$I EZDSLDEF.INC}
  487.     {---Place any compiler options you require here----------}
  488.  
  489.  
  490.     {--------------------------------------------------------}
  491.     {$I EZDSLOPT.INC}
  492.  
  493. The include file EZDSLDEF.INC contains the compiler defines for the
  494. EZDSL library. These defines are described below. The include file
  495. EZDSLOPT.INC contains the _unchangeable_ compiler options for the
  496. EZDSL library; by unchangeable I mean that if you do change one or two
  497. then EZDSL might still work, but on the other hand it might not. Any
  498. other defines you can change at will, and EZDSL will compile and run.
  499. These changeable options should be placed inside the indicated place.
  500.  
  501. The DEBUG compiler define has already been mentioned, but there are
  502. also two other compiler defines from EZDSLDEF.INC to consider. Here is
  503. the full list.
  504.  
  505. DEBUG defines whether the unit will be compiled with debug information
  506. (if on it automatically sets $D+ and $L+, if off these options are set
  507. off) and with assertion checks activated.  It is on by default.
  508.  
  509. UseTreeRecursion affects binary trees. If the define is activated (as
  510. it is by default) then any binary tree routines that navigate through
  511. the tree will use recursive algorithms to do it. If the define is
  512. deactivated the routines will remove any navigation recursion by
  513. using a TStack (this is known as unrolling the recursion). The pros
  514. and cons are that recursive routines are simple and fast but can use
  515. a lot of internal stack space (and could end up blowing the stack
  516. should your $M directive be small enough in Delphi 1.0), whereas
  517. unrolling the recursion is slower but does not require a lot of
  518. internal stack space, relying instead on the Pascal heap via a TStack
  519. container. It can also be difficult to estimate the amount of stack
  520. space required for a recursive navigation process on a large binary
  521. tree.
  522.  
  523. SuppressWarnings is a Delphi 2.0 only compiler define. When active (as
  524. it is by default) all warnings that are generated for the code in
  525. EZDSL are suppressed and you won't see them; when inactive the
  526. warnings are shown as normal. I have found the Delphi 2.0 warnings to
  527. be a mixed blessing: they are great at finding those silly but simple
  528. mistakes we all make (eg forgetting to set a function result) but they
  529. can be fooled by moderately complex code. There are several places in
  530. EZDSL where the compiler generates a warning, but which can be shown
  531. to be false. Maybe you'll have fun turning off the SuppressWarnings
  532. define and working out why the compiler is wrong for the warnings that
  533. are then shown.
  534.  
  535.  
  536. Of Compilers and Caveats
  537. ----------------------------------------------------------------------
  538.  
  539. I have tested the EZDSL unit with Borland Delphi 1.02 and Borland
  540. Delphi 2.0.
  541.  
  542. The EZDSL library has grown out of both my own personal work and my
  543. work at TurboPower. If you are familiar with TurboPower products you
  544. may find echoes of TBigCollection in TEZCollection, and the iterator
  545. idea is lifted from Orpheus' sparse array class.  In return the
  546. container classes in Win/Sys for Delphi (written by Kim Kokkonen) take
  547. some of the ideas from EZDSL and move them further. You could say
  548. EZDSL reflects my programming and coding practices, and is hence
  549. somewhat Julian-centric!
  550.  
  551.  
  552. Example programs
  553. ----------------------------------------------------------------------
  554.  
  555. To see how to use the EZSTRUCS unit, check out the example units and
  556. test programs in the same archive file.
  557.  
  558.  
  559. Programming Documentation
  560. ----------------------------------------------------------------------
  561.  
  562. The next section contains all the documented container classes and
  563. their documented methods and fields. For the underlying undocumented
  564. classes, fields, methods and algorithms see the source code (Use the
  565. Source, Luke!).
  566.  
  567. It might be beneficial to review the naming convention for the methods
  568. of these containers; it'll also provide a summary of the important
  569. methods to know.
  570.  
  571. Create  creates a new instance of the container, and prepares it for
  572.         use. You define whether the container is to 'own' its data
  573.         objects or whether it is just holding a reference to them.
  574.  
  575. Destroy destroys an object instance, releasing all memory used by the
  576.         container including that held by the remaining data objects
  577.         in the container (providing of course that the container owns
  578.         its objects).
  579.  
  580. Insert  inserts a data object into the container. For some containers
  581.         there might also be other InsertXxxxx methods that insert data
  582.         objects in certain other defined ways. For stacks and queues
  583.         Insert is known as Push or Append.
  584.  
  585. Delete  unlinks a data object from a container but does not dispose of
  586.         the memory held by the data object. For stacks and queues
  587.         Delete is known as Pop.
  588.  
  589. Erase   unlinks a data object from a container and also disposes the
  590.         memory held by the object (if the container owns its data
  591.         objects, that is).
  592.  
  593. Examine returns the data object at the 'current position' of the
  594.         container. Current position is defined in different ways for
  595.         different containers: for a stack or queue it is the head of
  596.         the stack or queue for example.
  597.  
  598. Empty   empties the container by calling Erase for all data objects.
  599.  
  600. IsEmpty returns true if there are no data objects in the container.
  601.  
  602. Iterate calls its action routine for each data object in the
  603.         container.
  604.  
  605. Clone   creates an exact duplicate of a data container. All the data
  606.         objects within the container are also duplicated if the new
  607.         container is going to be a data owner, else only the pointers
  608.         to the data objects are copied over. Note that an "exact
  609.         lookalike" copy might not be created, a clone of a binary
  610.         search tree might not look the same, even though all the nodes
  611.         are in sorted InOrder sequence.
  612.  
  613. Join    adds all the data objects in one container to another (in a
  614.         fashion that makes sense according to the container type). The
  615.         emptied container is disposed of.
  616.  
  617. Split   splits a container into two, moving all the data objects from
  618.         the split point to a newly created container of the same type
  619.         as the first. In this version of EZDSL, Split has not been
  620.         implemented for binary trees.
  621.  
  622. ══════════════════════════════════════════════════════════════════════
  623.  
  624. Global Types, Constants and Variables
  625. =====================================
  626.  
  627. const
  628.   ezdsStartOffset = $E2D5;
  629.   escTooManyItems   = ezdsStartOffset+1;
  630.   escInsInvalidHere = ezdsStartOffset+2;
  631.   escDelInvalidHere = ezdsStartOffset+3;
  632.   escInsertDup      = ezdsStartOffset+4;
  633.   escTreeStackError = ezdsStartOffset+5;
  634.   escTreeQueueError = ezdsStartOffset+6;
  635.   escCannotMoveHere = ezdsStartOffset+7;
  636.   escIncompatible   = ezdsStartOffset+8;
  637.   escNoCompare      = ezdsStartOffset+9;
  638.   escNoDupData      = ezdsStartOffset+10;
  639.   escNoDisposeData  = ezdsStartOffset+11;
  640.   escBadSource      = ezdsStartOffset+12;
  641.   ascFreeNilNode    = ezdsStartOffset+20;
  642.   ascNewNodeSize0   = ezdsStartOffset+21;
  643.   ascFreeNodeSize0  = ezdsStartOffset+22;
  644.   ascEmptyExamine   = ezdsStartOffset+23;
  645.   ascEmptyPop       = ezdsStartOffset+24;
  646.   ascDeleteEdges    = ezdsStartOffset+25;
  647.   ascExamineEdges   = ezdsStartOffset+26;
  648.   ascInsertEdges    = ezdsStartOffset+27;
  649.   ascReplaceEdges   = ezdsStartOffset+28;
  650.   ascAlreadyAtEnd   = ezdsStartOffset+29;
  651.   ascAlreadyAtStart = ezdsStartOffset+30;
  652.   ascCannotJoinHere = ezdsStartOffset+31;
  653.   ascCannotJoinData = ezdsStartOffset+32;
  654.   ascSplitEdges     = ezdsStartOffset+33;
  655.   ascOutOfRange     = ezdsStartOffset+34;
  656.   ascExamineLeaf    = ezdsStartOffset+35;
  657.   ascBadSkipLevel   = ezdsStartOffset+36;
  658.  
  659. Various string constants defining error and assertion conditions.
  660. The strings themselved are held in EZDSLCTS.RES in a stringtable. As
  661. Windows only allows one stringtable per application ezdsStartOffset
  662. can be altered to any constant value so that the string constants
  663. don't clash with your current application.
  664.  
  665. const
  666.   skMaxLevels = 16;
  667.  
  668. The maximum number of levels in a skip list. A skip list node will
  669. never have more than this number of forward pointers.
  670.  
  671. type
  672.   TChild = (CLeft, CRight);
  673.  
  674. For binary trees: flags for left and right children.
  675.  
  676. type
  677.   TCompareFunc = function (Data1, Data2 : pointer) : integer;
  678.  
  679. Function prototype for comparing two data objects. The function must
  680. return a negative number if Data1 is less than Data2, 0 if they are
  681. equal, and a positive number if Data1 is greater than Data2.
  682.  
  683. type
  684.   TDisposeDataProc = procedure (aData : pointer);
  685.  
  686. Procedure prototype for disposing a data object.
  687.  
  688. type
  689.   TDupDataFunc = function (aData : pointer) : pointer;
  690.  
  691. Function prototype for duplicating data objects. The function must
  692. create a duplicate to the aData data object and return it as the
  693. function result.  If the duplication fails for some reason, then the
  694. function must raise an exception.
  695.  
  696. type
  697.   TIterator = function  (C : TAbstractContainer; aData : pointer;
  698.                          ExtraData : pointer) : boolean;
  699.  
  700. Function prototype for an Iterate method iterator. C is the
  701. container whose Iterate method was called. aData is the
  702. current data object.  ExtraData is the extra pointer you passed to
  703. Iterate. The function must return true if Iterate is to continue
  704. iterating, false if Iterate is to stop immediately.
  705.  
  706. type
  707.   TListCursor = longint;
  708.  
  709. Navigation cursor for TDList and TSkipList (double linked & skip
  710. lists).
  711.  
  712. type
  713.   TTraversalType = (ttPreOrder, ttInOrder, ttPostOrder, ttLevelOrder);
  714.  
  715. For binary trees: the different methods of traversing their nodes.
  716.  
  717. type
  718.   TTreeCursor = longint;
  719.  
  720. Navigation cursor for TBinTree and descendants (binary trees).
  721.  
  722. type
  723.   TEZString = string[255];
  724.   PEZString = ^TEZString;
  725.  
  726. Essentially for compatibility between Delphi and Delphi 2.0: these
  727. provide a much needed single type for short strings.
  728.  
  729. ──────────────────────────────────────────────────────────────────────
  730.  
  731. Routines (all within EZDSLSUP.PAS)
  732. ========
  733.  
  734. Declaration
  735.   function  EZIntCompare(Data1, Data2 : pointer) : integer;
  736. Description
  737.   Intended as a compare function for containers: compares two
  738.   longints.
  739.  
  740. Declaration
  741.   procedure EZIntDisposeData(aData : pointer);
  742. Description
  743.   Intended as a data disposal procedure for containers: disposes a
  744.   longint. In other words, does nothing!
  745.  
  746. Declaration
  747.   function  EZIntDupData(aData : pointer) : pointer;
  748. Description
  749.   Intended as a data duplication function for containers:  duplicates
  750.   a longint. Essentially it returns aData.
  751.  
  752. Declaration
  753.   function  EZStrNew(const S : string) : PEZString;
  754. Description
  755.   Allocates a memory block on the heap, copies S to it, and returns
  756.   the pointer to the memory block. Exactly equivalent to NewStr in
  757.   Delphi 1.0. In Delphi 2.0 EZStrNew provides a pointer to a short
  758.   string, not a long string.
  759.  
  760. Declaration
  761.   procedure EZStrDispose(PS : PEZString);
  762. Description
  763.   Disposes of a string allocated on the heap by EZStrNew.
  764.  
  765. Declaration
  766.   function  EZStrCompare(Data1, Data2 : pointer) : integer;
  767. Description
  768.   Intended as a compare function for containers: compares two
  769.   strings in case-sensitive manner. The strings are assumed to have
  770.   been assigned with the EZStrNew routine, in other words are short
  771.   strings in Delphi 2.0.
  772.  
  773. Declaration
  774.   procedure EZStrDisposeData(aData : pointer);
  775. Description
  776.   Intended as a data disposal procedure for containers: disposes a
  777.   string. The string is assumed to have been assigned with the
  778.   EZStrNew routine.
  779.  
  780. Declaration
  781.   function  EZStrDupData(aData : pointer) : pointer;
  782. Description
  783.   Intended as a data duplication function for containers:  duplicates
  784.   a string. The string is assumed to have been assigned with the
  785.   EZStrNew.
  786.  
  787. ──────────────────────────────────────────────────────────────────────
  788.  
  789. TAbstractContainer (EZDSLBSE.PAS)
  790. ==================
  791.  
  792. This type is an abstract container class, an ancestor to all the
  793. other containers. Most methods have to be or must be overridden, but
  794. it forms a base object from which more complex objects can be
  795. derived.  Please review the descriptions of the properties defined
  796. below; the most important are Compare, and DisposeData; DupData is
  797. only required if you are going to be cloning containers.
  798.  
  799. Do *not* create an instance of this object type.
  800.  
  801. Properties
  802. ----------
  803.  
  804. Declaration
  805.   property Count : longint
  806. Description
  807.   READ ONLY. The number of data objects in the container.
  808.  
  809. Declaration
  810.   property Compare : TCompareFunc
  811. Description
  812.   The container's Compare function. If you don't 'override' it, the
  813.   default one raises an exception.
  814.  
  815. Declaration
  816.   property DisposeData : TDisposeDataProc
  817. Description
  818.   The container's DisposeData procedure. If you don't 'override' it,
  819.   the default one raises an exception.
  820.  
  821. Declaration
  822.   property DupData : TDupDataFunc
  823. Description
  824.   The container's DupData function. If you don't 'override' it, the
  825.   default one raises an exception.
  826.  
  827. Declaration
  828.   property IsDataOwner : boolean
  829. Description
  830.   READ ONLY. True if the container was created as a data owner, False
  831.   otherwise. It is not possible to change this property after the
  832.   container has been created.
  833.  
  834.  
  835. Interfaced methods
  836. ------------------
  837.  
  838. Declaration
  839.   constructor Create(DataOwner : boolean); virtual;
  840. Description
  841.   Creates the object. Descendants must set the NodeSize field before
  842.   calling this as inherited constructor. If non-zero, this method
  843.   gets a pointer to the relevent node store (if zero the descendant
  844.   will be taking care of allocating/freeing nodes).  Sets the
  845.   internal item counter to zero.  The DataOwner parameter determines
  846.   whether the container is to own (ie can dispose of) its data
  847.   objects.
  848.  
  849. Declaration
  850.   destructor Destroy; override;
  851. Description
  852.   Destroys the container by calling the Empty method, detaches itself
  853.   from the node store.
  854.  
  855. Declaration
  856.   constructor Clone(Source : TAbstractContainer;
  857.                     DataOwner : boolean; NewCompare : TCompareFunc);
  858.                                                     virtual; abstract;
  859. Description
  860.   Constructor to create a 'clone' (ie an exact copy) of the Source
  861.   container and all its data objects.  Descendants will override this
  862.   constructor without fail. If you are going to use this method you
  863.   *must* override the DupData function as well.  You may specify a
  864.   new Compare function for the cloned container, in which case, if
  865.   the container maintains a sorted order, the new container will have
  866.   its data objects in another order. If DataOwner is true, the new
  867.   container will own its data objects and hence all the objects in
  868.   the original container will be duplicated.
  869.  
  870. Declaration
  871.   procedure Empty; virtual; abstract;
  872. Description
  873.   Abstract method that empties the container; each container
  874.   descendant will have its own preferred efficient method of doing
  875.   this. NOTE: DisposeData will be called for all objects in the
  876.   container if the container owns its data objects.
  877.  
  878. Declaration
  879.   function IsEmpty : boolean;
  880. Description
  881.   Returns true if the container is empty; ie it contains no data
  882.   objects.
  883.  
  884.  
  885. ──────────────────────────────────────────────────────────────────────
  886.  
  887. TStack (EZDSLSTK.PAS)
  888. ======
  889.  
  890. A stack is a LIFO container (last in first out): the last data object
  891. pushed on will be the first to be popped off. You can also examine
  892. (peek at) the next item to be popped off. However you cannot navigate
  893. through the stack, the data objects underneath the top one are hidden
  894. from you until you pop the ones above off.
  895.  
  896. Interfaced Methods
  897. ------------------
  898.  
  899. Declaration
  900.   constructor Create(DataOwner : boolean); override;
  901. Description
  902.   Creates the stack by calling the ancestor's Create after setting a
  903.   node size of 8.  If DataOwner is true, the new stack will own its
  904.   data objects.
  905.  
  906. Declaration
  907.   constructor Clone(Source : TAbstractContainer;
  908.                     DataOwner : boolean; NewCompare : TCompareFunc);
  909.                                                             override;
  910. Description
  911.   Creates a copy of a Source stack.
  912.  
  913. Declaration
  914.   procedure Empty; override;
  915. Description
  916.   Repeatedly calls the Pop method (disposing of the data object
  917.   returned if a data owner) until the stack is empty.
  918.  
  919. Declaration
  920.   function Examine : pointer;
  921. Description
  922.   Returns the data object at the top of the stack without popping it.
  923.   If the stack is empty, nil is returned.
  924.  
  925. Declaration
  926.   function Pop : pointer;
  927. Description
  928.   Pops the data object from the top of the stack and returns it.  In
  929.   DEBUG mode, an assertion error will occur if the stack is empty.
  930.  
  931. Declaration
  932.   procedure Push(aData : pointer);
  933. Description
  934.   Pushes the data object aData onto the top of the stack.
  935.  
  936. ──────────────────────────────────────────────────────────────────────
  937.  
  938. TQueue (EZDSLQUE.PAS)
  939. ======
  940.  
  941. A queue is a FIFO container (first in first out): the first object put
  942. in the queue will be the first popped, the last object will be the
  943. last to be popped. You can examine (peek at) the next data object to
  944. be popped.  However you cannot navigate through the queue.
  945.  
  946. Interfaced Methods
  947. ------------------
  948.  
  949. Declaration
  950.   constructor Create(DataOwner : boolean); override;
  951. Description
  952.   Creates the queue by calling the ancestor's Create after setting a
  953.   node size of 8.  If DataOwner is true, the new queue will own its
  954.   data objects.
  955.  
  956. Declaration
  957.   procedure Append(aData : pointer);
  958. Description
  959.   Adds the data object aData to the end of the queue.
  960.  
  961. Declaration
  962.   constructor Clone(Source : TAbstractContainer;
  963.                     DataOwner : boolean; NewCompare : TCompareFunc);
  964.                                                             override;
  965. Description
  966.   Creates a copy of the Source queue.
  967.  
  968. Declaration
  969.   procedure Empty; override;
  970. Description
  971.   Repeatedly calls the Pop method (disposing of the data object
  972.   returned if a data owner) until the queue is empty.
  973.  
  974. Declaration
  975.   function Examine : pointer;
  976. Description
  977.   Returns the data from the top of the queue without popping it. If
  978.   the stack is empty, nil is returned without causing an error.
  979.  
  980. Declaration
  981.   function Pop : pointer;
  982. Description
  983.   Pops the data object from the front of the queue and returns it.
  984.   In DEBUG mode, an assertion error will occur if the queue is empty.
  985.  
  986. ──────────────────────────────────────────────────────────────────────
  987.  
  988. TDeque
  989. ======
  990.  
  991. A deque (sometimes pronounced DECK, sometimes DEQUEUE) is a queue
  992. that allows objects to be added to, or removed from the front or back
  993. of the queue. This particular implementation of a deque just allows
  994. queue jumpers, ie data objects can also be pushed into the front of
  995. the queue, giving it stack-like behaviour (Flamig calls this variant
  996. a Staque, see references). It is descended from the basic TQueue and
  997. inherits Pop and Append.
  998.  
  999. Interfaced Methods
  1000. ------------------
  1001.  
  1002. Declaration
  1003.   procedure Push(aData : pointer);
  1004. Description
  1005.   Pushes the data object aData to the front of the deque.
  1006.  
  1007. ──────────────────────────────────────────────────────────────────────
  1008.  
  1009. TPriorityQueue
  1010. ==============
  1011.  
  1012. A priority queue is much like an ordinary queue, except that the
  1013. smallest data object in the queue will be popped first (rather than
  1014. the 'oldest'). Another name for a priority queue is a heap (not to be
  1015. confused with the Pascal heap where memory blocks are allocated and
  1016. freed).  As it imposes a sort order on the data objects, you must
  1017. override the Compare function.
  1018.  
  1019. If the Compare method returns values in the 'normal' sense (ie
  1020. Compare returns a negative number if Data1 < Data2, 0 if Data1 =
  1021. Data2, and a positive number otherwise), then data objects will be
  1022. popped off smallest first, ie in increasing order. However, if
  1023. Compare returns values in the 'reverse' sense (ie returning negative
  1024. if Data1 > Data2, etc), then elements will be popped off largest
  1025. first, ie in decreasing order. Thus by carefully selecting Compare,
  1026. this object will provide the classic min-heap and max-heap data
  1027. structures.
  1028.  
  1029. Notice that the well-known Heap Sort algorithm uses a structure of
  1030. this type, and in fact this structure could be used to provide a
  1031. generic sort routine. It will be faster than using a skip list for
  1032. example (the data objects are not held internally in a fully sorted
  1033. manner, they are just sorted in a 'loose' sense).
  1034.  
  1035. Interfaced Methods
  1036. ------------------
  1037.  
  1038. Declaration
  1039.   constructor Create(DataOwner : boolean);
  1040. Description
  1041.   Creates the queue by calling the ancestor's Create after setting a
  1042.   node size of 16.  If DataOwner is true, the new queue will own its
  1043.   data objects.
  1044.  
  1045. Declaration
  1046.   procedure Append(aData : pointer);
  1047. Description
  1048.   Adds the data object aData to the priority queue.
  1049.  
  1050. Declaration
  1051.   constructor Clone(Source : TAbstractContainer;
  1052.                     DataOwner : boolean; NewCompare : TCompareFunc);
  1053.                                                             override;
  1054. Description
  1055.   Creates a copy of the Source queue.
  1056.  
  1057. Declaration
  1058.   procedure Empty; override;
  1059. Description
  1060.   Repeatedly calls the Pop method (disposing of the data object
  1061.   returned if a data owner) until the queue is empty.
  1062.  
  1063. Declaration
  1064.   function Examine : pointer;
  1065. Description
  1066.   Returns the data object from the top of the queue without popping
  1067.   it. If the queue is empty, nil is returned.
  1068.  
  1069. Declaration
  1070.   function Pop : pointer;
  1071. Description
  1072.   Pops the data object from the front of the queue and returns it.
  1073.   If Compare works in the 'normal' sense (ie returns a negative value
  1074.   for 'less than') then the data object will be the smallest,
  1075.   otherwise the data object will be the largest.  In DEBUG mode, an
  1076.   assertion error will occur if the queue is empty.
  1077.  
  1078. Declaration
  1079.   function Replace(aData : pointer) : pointer;
  1080. Description
  1081.   Equivalent to an Append followed by a Pop, but faster. Note that
  1082.   the same data object could be returned (it maybe smaller (larger)
  1083.   than all the data objects in the queue).
  1084.  
  1085. ──────────────────────────────────────────────────────────────────────
  1086.  
  1087. TLinkList
  1088. =========
  1089.  
  1090. A linked list is a container which is a chain of data objects. From a
  1091. given position in the list you can move forwards or backwards to the
  1092. next or previous data object. You cannot directly jump to the Nth
  1093. data object, instead you have to walk the list from the beginning
  1094. counting as you go. This list implementation has an implied 'cursor'
  1095. which points to the current data object, you move this cursor along
  1096. the list by means of the list's methods (eg Next and Prev). You can
  1097. examine the data object that the cursor is pointing to, or insert a
  1098. new one before or after the cursor, or delete the data object at the
  1099. cursor (unlink it).
  1100.  
  1101. There are two special cursor positions associated with this list:
  1102. before the first data object (known as BeforeFirst) and after the
  1103. last data object (known as AfterLast). Even in an empty list these
  1104. are deemed different. In the diagram of a linked list below the three
  1105. data objects are a, b and c, and the internal cursor is pointing at
  1106. c:
  1107.  
  1108.        BeforeFirst --> a --> b --> c --> AfterLast
  1109.  
  1110.                                    |
  1111.        Cursor ---------------------+
  1112.  
  1113.  
  1114. As a convenience to the programmer, this implementation of a linked
  1115. list allows the data objects to be maintained in a sorted order. To
  1116. do this there are some practical and some theoretical considerations.
  1117. The practical ones first: you must override Compare and you must use
  1118. InsertSorted to insert data objects into the list. The theoretical
  1119. considerations are to do with the linear sequential nature of a
  1120. linked list: to find where to insert a data object the list must
  1121. start at the beginning of the list and walk the list calling Compare
  1122. for each data object it encounters until it finds the place where the
  1123. new data object can be inserted. A time-consuming process as you can
  1124. appreciate.  You should only use sorted linked lists if the list is
  1125. built only once or if the number of data objects in the list is
  1126. likely to be small.
  1127.  
  1128. This particular implementation is a singly-linked list (each node has
  1129. just a single link to the 'next' node), however it uses a particular
  1130. algorithm that enables it to have the performance capabilities of a
  1131. doubly-linked list (see below) without the overhead of the extra
  1132. links.
  1133.  
  1134. Properties
  1135. ----------
  1136.  
  1137. Declaration
  1138.   property IsSorted : boolean
  1139. Description
  1140.   READ ONLY. IsSorted is true if the list is currently sorted (ie all
  1141.   inserts have been made with InsertSorted, or the list is empty).
  1142.   Note that to maintain data objects in sorted order you must supply
  1143.   an overridden Compare function and use the InsertSorted method;
  1144.   perhaps more importantly keeping a linked list in sorted order
  1145.   through many Inserts and Deletes is very inefficient, because of
  1146.   the sequential nature of the list.
  1147.  
  1148. Interfaced Methods
  1149. ------------------
  1150.  
  1151. Declaration
  1152.   constructor Create(DataOwner : boolean);
  1153. Description
  1154.   Creates the object by calling the ancestor's Create after setting a
  1155.   node size of 8.  If DataOwner is true, the new list will own its
  1156.   data objects.
  1157.  
  1158. Declaration
  1159.   constructor Clone(Source : TAbstractContainer;
  1160.                     DataOwner : boolean; NewCompare : TCompareFunc);
  1161.                                                             override;
  1162. Description
  1163.   Creates a copy of the Source list. If the original list was sorted
  1164.   then the cloned list is also sorted.
  1165.  
  1166. Declaration
  1167.   procedure Delete;
  1168. Description
  1169.   Unlinks the current data object but does not dispose of it. The
  1170.   internal cursor is moved to the next data object in the list, or to
  1171.   the AfterLast position if there are no more objects after it.  In
  1172.   DEBUG mode, an assertion error will occur if the cursor is at
  1173.   BeforeFirst or AfterLast.
  1174.  
  1175. Declaration
  1176.   procedure Empty; override;
  1177. Description
  1178.   Walks the list, calling Erase for every data object it finds.
  1179.  
  1180. Declaration
  1181.   procedure Erase;
  1182. Description
  1183.   Works like Delete, but also disposes the data object by calling
  1184.   DisposeData if the list owns its data objects.
  1185.  
  1186. Declaration
  1187.   function Examine : pointer;
  1188. Description
  1189.   Returns the data object the cursor is pointing to.  In DEBUG mode,
  1190.   an assertion error will occur if the cursor is at BeforeFirst or
  1191.   AfterLast.
  1192.  
  1193. Declaration
  1194.   function Iterate(Action : TIterator;
  1195.                    Backwards : boolean;
  1196.                    ExtraData : pointer) : pointer;
  1197. Description
  1198.   Walks the list from the start (if Backwards is False) or from the
  1199.   end (if Backwards is True) and calls Action for each data object
  1200.   found.  If Action returns False for a data object, then Iterate
  1201.   immediately returns with that data object; if Action always returns
  1202.   True then this method will return nil.  ExtraData is a pointer to
  1203.   any other data that you may want Action to use.
  1204.  
  1205. Declaration
  1206.   procedure InsertAfter(aData : pointer);
  1207. Description
  1208.   Inserts the data object aData after the cursor. If the list is
  1209.   currently sorted, calling this method forces it to an unsorted
  1210.   state. Use InsertSorted to maintain a sorted list. In DEBUG mode,
  1211.   an assertion error will occur if the cursor is at AfterLast.
  1212.  
  1213. Declaration
  1214.   procedure InsertBefore(aData : pointer);
  1215. Description
  1216.   Inserts the data object aData before the cursor. If the list is
  1217.   currently sorted, calling this method forces it to an unsorted
  1218.   state. Use InsertSorted to maintain a sorted list. In DEBUG mode,
  1219.   an assertion error will occur if the cursor is at BeforeFirst.
  1220.  
  1221. Declaration
  1222.   procedure InsertSorted(aData : pointer);
  1223. Description
  1224.   Inserts the data object aData in the correct place in the list's
  1225.   sequence. Only works if ALL data objects are so inserted; if the
  1226.   list is currently unsorted InsertBefore or InsertAfter is called
  1227.   instead.  Requires Compare to be overridden. *Not* efficient for
  1228.   long lists, you should use a skip list or binary search tree
  1229.   instead.
  1230.  
  1231. Declaration
  1232.   function IsAfterLast : boolean;
  1233. Description
  1234.   Returns true if the cursor is after all data objects in the list
  1235.   (beyond the end of the list).
  1236.  
  1237. Declaration
  1238.   function IsBeforeFirst : boolean;
  1239. Description
  1240.   Returns true if the cursor is before all data objects in the list.
  1241.  
  1242. Declaration
  1243.   procedure Join(List : TLinkList);
  1244. Description
  1245.   Adds all the data objects in List to the current list, placing them
  1246.   after the cursor. List is then destroyed. To add the data objects
  1247.   before all the current ones, call SetBeforeFirst first; to add them
  1248.   after all the current ones, call SetAfterLast and Prev first. Note
  1249.   that if the destination list is sorted, the nodes from List are
  1250.   added in sorted order. In DEBUG mode, an assertion error will occur
  1251.   if the cursor is at AfterLast.  Please note that both lists
  1252.   concerned must either be data owners or not.  You cannot Join a
  1253.   list that is a data owner to one that is not.
  1254.  
  1255. Declaration
  1256.   procedure Next;
  1257. Description
  1258.   Moves the cursor to the next data object in the list. In DEBUG
  1259.   mode, an assertion error will occur if the cursor is at AfterLast.
  1260.  
  1261. Declaration
  1262.   procedure Prev;
  1263. Description
  1264.   Moves the cursor to the previous data object in the list. In DEBUG
  1265.   mode, an assertion error will occur if the cursor is at
  1266.   BeforeFirst.
  1267.  
  1268. Declaration
  1269.   function Replace(aData : pointer) : pointer;
  1270. Description
  1271.   Replaces the data object at the cursor with aData and returns the
  1272.   replaced data object. Note that for sorted lists, the cursor might
  1273.   be moved. In DEBUG mode an assertion error will occur if the cursor
  1274.   is currently at BeforeFirst or AfterLast.
  1275.  
  1276. Declaration
  1277.   function Search(aData : pointer) : boolean;
  1278. Description
  1279.   Returns true if the data object aData is found in the list (Compare
  1280.   must return 0 between it and one of the data objects in the list),
  1281.   if not (and the list is sorted) it leaves the cursor just past
  1282.   where the data object could be inserted.
  1283.  
  1284. Declaration
  1285.   procedure SetAfterLast;
  1286. Description
  1287.   Moves the cursor after all the data objects in the list. Calling
  1288.   Prev from this position gives you the last object on the list.
  1289.  
  1290. Declaration
  1291.   procedure SetBeforeFirst;
  1292. Description
  1293.   Moves the cursor before all the data objects in the list. Calling
  1294.   Next from this position gives you the first object on the list.
  1295.  
  1296. Declaration
  1297.   function Split : TLinkList;
  1298. Description
  1299.   Splits the linked list into two at the cursor by creating a new
  1300.   list, and moving all the data objects from the cursor onwards to
  1301.   the new list. In DEBUG mode, an assertion error will occur if the
  1302.   cursor is at BeforeFirst or AfterLast (ie at least ONE data object
  1303.   must be moved; you cannot call Split for an empty list).
  1304.  
  1305. ──────────────────────────────────────────────────────────────────────
  1306.  
  1307. TDList
  1308. ======
  1309.  
  1310. This is a linked list of data objects. Compared with TLinkList, this
  1311. implementation uses external 'cursors' which point to the various
  1312. data objects, you move these cursors along the list by means of the
  1313. list's methods. You can examine the data object that a cursor is
  1314. pointing to, or insert a new one before or after a given cursor, or
  1315. delete the data object at a cursor (unlink it). Again, there are two
  1316. special cursor positions: before the first data object (BeforeFirst)
  1317. and after the last data object (AfterLast). Even in an empty list
  1318. these are different.
  1319.  
  1320. The difference between this object and TList is in the use of
  1321. external cursors, and also in the internal implementation. This
  1322. object uses nodes with two links (hence 'doubly linked') rather than
  1323. one.
  1324.  
  1325. Notice that it is up to you to make sure that your external cursors
  1326. are valid. The following code is deemed a programming error:
  1327.  
  1328.   begin
  1329.     {...}
  1330.     with MyDList do
  1331.       begin
  1332.         Cursor := Next(SetBeforeFirst);
  1333.         NextCursor := Delete(Cursor);
  1334.         if (Next(Cursor) = NextCursor) then   {<=== CRASH}
  1335.           {...}
  1336.  
  1337. Here, the value of Cursor is undefined after the call to Delete. This
  1338. is much the same as using pointers: you may have several copies of a
  1339. pointer to a heap memory block, but as soon as you free that memory
  1340. block all those pointer variables are invalid.
  1341.  
  1342. As a convenience to the programmer, this implementation of a doubly
  1343. linked list allows the data objects to be maintained in a sorted
  1344. order, much as for TLinkList. Please see the discussion of sorted
  1345. lists above in that section.
  1346.  
  1347. Properties
  1348. ----------
  1349.  
  1350. Declaration
  1351.   property IsSorted : boolean
  1352. Description
  1353.   READ ONLY. Is true if the list is currently sorted (ie all inserts
  1354.   have been made with InsertSorted, or the list is empty).
  1355.  
  1356. Interfaced Methods
  1357. ------------------
  1358.  
  1359. Declaration
  1360.   constructor Create(DataOwner : boolean);
  1361. Description
  1362.   Creates the object by calling the ancestor's Create after setting a
  1363.   node size of 12.  If DataOwner is true, the new list will own its
  1364.   data objects.
  1365.  
  1366. Declaration
  1367.   constructor Clone(Source : TAbstractContainer;
  1368.                     DataOwner : boolean; NewCompare : TCompareFunc);
  1369.                                                             override;
  1370. Description
  1371.   Creates a exact copy of the Source list. If the original list was
  1372.   sorted then the cloned list is also sorted.
  1373.  
  1374. Declaration
  1375.   function Delete(Cursor : TListCursor) : TListCursor;
  1376. Description
  1377.   Unlinks the passed cursor but does not dispose of its data object.
  1378.   The cursor of the next data object in the list is returned. Please
  1379.   note that the parameter Cursor is invalid after this routine is
  1380.   called. In DEBUG mode, an assertion error will occur if Cursor is
  1381.   at BeforeFirst or AfterLast.
  1382.  
  1383. Declaration
  1384.   procedure Empty; override;
  1385. Description
  1386.   Walks the list, calling Erase for every data object it finds.
  1387.  
  1388. Declaration
  1389.   function Erase(Cursor : TListCursor) : TListCursor;
  1390. Description
  1391.   Works like Delete, but also disposes the data object by calling
  1392.   DisposeData, providing the list owns its data objects.
  1393.  
  1394. Declaration
  1395.   function Examine(Cursor : TListCursor) : pointer;
  1396. Description
  1397.   Returns the data object the cursor is pointing to. In DEBUG mode,
  1398.   an assertion error will occur if Cursor is at BeforeFirst or
  1399.   AfterLast.
  1400.  
  1401. Declaration
  1402.   function Iterate(Action : TIteratorFunc;
  1403.                    Backwards : boolean;
  1404.                    ExtraData : pointer) : TListCursor;
  1405. Description
  1406.   Walks the list from the first data object (if Backwards is False)
  1407.   or from the last data object (if Backwards is True) and calls
  1408.   Action for each data object found.  If Action returns False, then
  1409.   this method immediately returns with that data object's cursor; if
  1410.   Action always returns True then this method will return 0.
  1411.   ExtraData is a pointer to any other data that you may want Action
  1412.   to use.
  1413.  
  1414. Description
  1415.   procedure InsertAfter(Cursor : TListCursor; aData : pointer);
  1416. Description
  1417.   Inserts the data object aData after Cursor. If the list is
  1418.   currently sorted, calling this method forces it to an unsorted
  1419.   state. Use InsertSorted to maintain a sorted list. In DEBUG mode,
  1420.   an assertion error will occur if the cursor is at AfterLast.
  1421.  
  1422. Declaration
  1423.   procedure InsertBefore(Cursor : TListCursor; aData : pointer);
  1424. Description
  1425.   Inserts the data object aData after Cursor. If the list is
  1426.   currently sorted, calling this method forces it to an unsorted
  1427.   state. Use InsertSorted to maintain a sorted list. In DEBUG mode,
  1428.   an assertion error will occur if the cursor is at AfterLast.
  1429.  
  1430. Declaration
  1431.   procedure InsertSorted(aData : pointer);
  1432. Description
  1433.   Inserts the data object aData in the correct place in the list's
  1434.   sequence. Only works if ALL data objects are so inserted; if the
  1435.   list is currently unsorted the data object is inserted at the front
  1436.   of the list. Requires Compare to be overridden. *Not* efficient for
  1437.   long lists, you should use a skip list or binary search tree
  1438.   instead.
  1439.  
  1440. Declaration
  1441.   function IsAfterLast(Cursor : TListCursor) : boolean;
  1442. Description
  1443.   Returns true if Cursor is after all data objects in the list.
  1444.  
  1445. Declaration
  1446.   function IsBeforeFirst(Cursor : TListCursor) : boolean;
  1447. Description
  1448.   Returns true if Cursor is before all data objects in the list.
  1449.  
  1450. Declaration
  1451.   procedure Join(Cursor : TListCursor; List : TDList);
  1452. Description
  1453.   Adds all the data objects in List to the current list, placing them
  1454.   after Cursor. List is then destroyed. To add the data objects
  1455.   before all the current ones, use SetBeforeFirst as cursor; to add
  1456.   them after all the current ones, use SetAfterLast and Prev as
  1457.   cursor.  Note that if the destination list is sorted, the nodes
  1458.   from List are added in sorted order. In DEBUG mode, an assertion
  1459.   error will occur if the cursor is at AfterLast.  Please note that
  1460.   both lists concerned must either be data owners or not.  You cannot
  1461.   Join a list that is a data owner to one that is not.
  1462.  
  1463. Declaration
  1464.   function Next(Cursor : TListCursor) : TListCursor;
  1465. Description
  1466.   Given a Cursor into the list, returns a cursor to the next data
  1467.   object. In DEBUG mode, an assertion error will occur if Cursor is
  1468.   at AfterLast.
  1469.  
  1470. Declaration
  1471.   function Prev(Cursor : TListCursor) : TListCursor;
  1472. Description
  1473.   Given a Cursor into the list, returns a cursor to the previous data
  1474.   object. In DEBUG mode, an assertion error will occur if Cursor is
  1475.   at BeforeFirst.
  1476.  
  1477. Declaration
  1478.   function Replace(Cursor : TListCursor; aData : pointer) :  pointer;
  1479. Description
  1480.   Replaces the data object at Cursor with aData and returns the
  1481.   replaced data object. In DEBUG mode an assertion error will occur if
  1482.   the cursor is currently at BeforeFirst or AfterLast.
  1483.  
  1484. Declaration
  1485.   function Search(var Cursor : TListCursor;
  1486.                       aData  : pointer) : boolean;
  1487. Description
  1488.   Returns true if the data object aData is found in the list (Compare
  1489.   must return 0 between it and an object in the list), and returns
  1490.   the cursor. If the data object was not found in a sorted list,
  1491.   Search returns the cursor just past where the data object should be
  1492.   inserted.
  1493.  
  1494. Declaration
  1495.   function SetAfterLast : TListCursor;
  1496. Description
  1497.   Returns the cursor that is after all the data objects in the list.
  1498.  
  1499. Declaration
  1500.   function SetBeforeFirst : TListCursor;
  1501. Description
  1502.   Returns the cursor that is before all the data objects in the list.
  1503.  
  1504. Declaration
  1505.   function Split(Cursor : TListCursor) : TDList;
  1506. Description
  1507.   Splits the linked list into two at Cursor by creating a new list,
  1508.   and moving all the data objects from Cursor onwards to the new list.
  1509.   In DEBUG mode, an assertion error will occur if Cursor is at
  1510.   BeforeFirst or AfterLast (ie at least ONE data object must be moved;
  1511.   you cannot call Split for an empty list).
  1512.  
  1513. ──────────────────────────────────────────────────────────────────────
  1514.  
  1515. TSkipList
  1516. =========
  1517.  
  1518. A skip list is a special type of linked list of data objects. The
  1519. list is sorted and therefore requires the Compare method to be
  1520. overridden.  Compared with TList and TDList, this implementation uses
  1521. nodes of varying sizes. The nodes have between 1 and 16 (skMaxLevels)
  1522. of forward pointers, the higher ones skipping over nodes with less
  1523. forward pointers. This means much faster search times, but slightly
  1524. slower list update times (ie insert and delete). Can cope with
  1525. searching long lists without too much degradation. Compared with a
  1526. red-black binary search tree, this type of data structure will
  1527. consume more memory, will have equivalent insert times and delete
  1528. times, and will have comparable (amortised) search times.
  1529.  
  1530. Like TList and TDList, the skip list has two special positions:
  1531. before all the data objects and after all the data objects.
  1532.  
  1533. To grasp how it works, start with a classic doubly linked list where
  1534. each node has a pointer to the next and previous nodes. Now imagine
  1535. that about one in four of these nodes has a pointer to the node 4
  1536. nodes ahead as well. Furthermore suppose that about one in sixteen of
  1537. the nodes has a pointer to the node 16 nodes ahead. And so on. As you
  1538. can see, you can navigate through the list pretty quickly, jumping
  1539. over lesser nodes and 'homing in' on the node you want. For example,
  1540. in the diagram below, to get to g quickly from BeforeFirst, you can
  1541. jump to d straightaway, followed by a smaller jump to f followed by a
  1542. small jump to g.
  1543.  
  1544.               -------------------------------------------->
  1545.               -------------------->   -------------------->
  1546.               -------->   -------->   -------->   -------->
  1547.   BeforeFirst --> a --> b --> c --> d --> e --> f --> g --> AfterLast
  1548.  
  1549.  
  1550. Interfaced Methods
  1551. ------------------
  1552.  
  1553. Declaration
  1554.   constructor Create(DataOwner : boolean);
  1555. Description
  1556.   Creates the object by calling the ancestor's Create after setting a
  1557.   node size of 0. This means that this object class will do its own
  1558.   allocating of nodes; the nodes are of varying sizes from 16 bytes
  1559.   to 76 bytes in 16 bit mode (from 20 to 80 bytes in 32-bit mode). If
  1560.   DataOwner is true, the new list will own its data objects.
  1561.  
  1562. Declaration
  1563.   constructor Clone(Source : TAbstractContainer;
  1564.                     DataOwner : boolean; NewCompare : TCompareFunc);
  1565.                                                             override;
  1566. Description
  1567.   Creates a copy of the Source skip list.
  1568.  
  1569. Declaration
  1570.   function Delete(Cursor : TListCursor) : TListCursor;
  1571. Description
  1572.   Unlinks the passed cursor but does not dispose of its data object.
  1573.   The cursor of the next data object in the list is returned. Please
  1574.   note that if there are several data objects that Compare equal to
  1575.   the data object at Cursor, the *first* of these will be deleted,
  1576.   not necessarily the one at Cursor.  Also note that the parameter
  1577.   Cursor is invalid after this routine is called. In DEBUG mode, an
  1578.   assertion error will occur if Cursor is at BeforeFirst or
  1579.   AfterLast.
  1580.  
  1581. Declaration
  1582.   procedure Empty; override;
  1583. Description
  1584.   Walks the list, calling Erase for every data object it finds.
  1585.  
  1586. Declaration
  1587.   function Erase(Cursor : TListCursor) : TListCursor;
  1588. Description
  1589.   Works like Delete, but also disposes the data object by calling
  1590.   DisposeData, providing the list owns its data objects.
  1591.  
  1592. Declaration
  1593.   function Examine(Cursor : TListCursor) : pointer;
  1594. Description
  1595.   Returns the data object the cursor is pointing to. In DEBUG mode,
  1596.   an assertion error will occur if Cursor is at BeforeFirst or
  1597.   AfterLast.
  1598.  
  1599. Declaration
  1600.   function Iterate(Action : TIteratorFunc;
  1601.                    Backwards : boolean;
  1602.                    ExtraData : pointer) : TListCursor;
  1603. Description
  1604.   Walks the list from the first data object (if Backwards is False)
  1605.   or from the last data object (if Backwards is True) and calls
  1606.   Action for each data object found.  If Action returns False, then
  1607.   this method immediately returns with that data object's cursor; if
  1608.   Action always returns True then this method will return 0.
  1609.   ExtraData is a pointer to any other data that you may want Action
  1610.   to use.
  1611.  
  1612. Declaration
  1613.   procedure Insert(var Cursor : TListCursor; aData : pointer);
  1614. Description
  1615.   Inserts the data object aData in the correct place in the list's
  1616.   sort sequence. Requires Compare to be overridden.
  1617.  
  1618. Declaration
  1619.   function IsAfterLast(Cursor : TListCursor) : boolean;
  1620. Description
  1621.   Returns true if the cursor is after all data objects in the list.
  1622.  
  1623. Declaration
  1624.   function IsBeforeFirst(Cursor : TListCursor) : boolean;
  1625. Description
  1626.   Returns true if the cursor is before all data objects in the list.
  1627.  
  1628. Declaration
  1629.   procedure Join(List : TSkipList);
  1630. Description
  1631.   Adds all the data objects in List to the current list, placing them
  1632.   in the correct order. List is then destroyed.  Please note that
  1633.   both lists concerned must either be data owners or not.  You cannot
  1634.   Join a list that is a data owner to one that is not.
  1635.  
  1636. Declaration
  1637.   function Next(Cursor : TListCursor) : TListCursor;
  1638. Description
  1639.   Given a Cursor into the list, returns a cursor to the next data
  1640.   object. In DEBUG mode, an assertion error will occur if Cursor is
  1641.   at AfterLast.
  1642.  
  1643. Declaration
  1644.   function Prev(Cursor : TListCursor) : TListCursor;
  1645. Description
  1646.   Given a Cursor into the list, returns a cursor to the previous data
  1647.   object. In DEBUG mode, an assertion error will occur if Cursor is
  1648.   at BeforeFirst.
  1649.  
  1650. Declaration
  1651.   function Replace(Cursor : TListCursor; aData : pointer) : pointer;
  1652. Description
  1653.   Replaces the data object at the cursor with aData and returns the
  1654.   replaced data object. Note that because this is a sorted list, this
  1655.   is equivalent to Delete followed by Insert (and is coded that way)
  1656.   and Cursor may not be pointing to the same data object afterwards.
  1657.  
  1658. Declaration
  1659.   function Search(var Cursor : TListCursor; aData : pointer)
  1660.                                                             : boolean;
  1661. Description
  1662.   Returns true if the data object aData is found in the list (Compare
  1663.   must return 0 between it and an object in the list), and returns the
  1664.   cursor. If the data object was not found in a sorted list, Search
  1665.   returns the cursor just past where the data object should be
  1666.   inserted.
  1667.  
  1668. Declaration
  1669.   function SetAfterLast : TListCursor;
  1670. Description
  1671.   Returns the cursor that is after all the data objects in the list.
  1672.  
  1673. Declaration
  1674.   function SetBeforeFirst : TListCursor;
  1675. Description
  1676.   Returns the cursor that is before all the data objects in the list.
  1677.  
  1678. Declaration
  1679.   function Split(Cursor : TListCursor) : TSkipList;
  1680. Description
  1681.   Splits the linked list into two at Cursor by creating a new list,
  1682.   and moving all the data objects from Cursor onwards to the new list.
  1683.   In DEBUG mode, an assertion error will occur if Cursor is at
  1684.   BeforeFirst or AfterLast (ie at least ONE data object must be moved;
  1685.   you cannot call Split for an empty list).
  1686.  
  1687. ──────────────────────────────────────────────────────────────────────
  1688.  
  1689. TBinTree
  1690. ========
  1691.  
  1692. A binary tree is a data structure that can best be described in a
  1693. recursive manner: a tree is either empty or consists of a node with
  1694. links to two other trees. Classically a binary tree is drawn:
  1695.  
  1696.                          Root
  1697.                         /    \
  1698.                        a      b
  1699.                       / \    / \
  1700.                      c   d  e   f
  1701.  
  1702. The node at the top of the tree is called the root node. The
  1703. implementation of a binary tree in EZSTRUCS uses the concepts of
  1704. internal and external nodes. An internal node of the tree always has
  1705. two children, an external node (also known as a leaf) has no
  1706. children. An external node carries no data object, only internal
  1707. nodes can be associated with data objects; if you like, external nodes
  1708. are like earth connections in electrical diagrams (in this
  1709. implementation external nodes are nil).  In the literature internal
  1710. nodes tend to be drawn with small circles and external nodes with
  1711. small squares.  In the example above Root, a and b are all internal
  1712. nodes; c, d, e, and f are external nodes.  Internal and external
  1713. nodes are important when you are inserting or deleting data objects.
  1714.  
  1715. Rule 1: you can only insert a data object at an external node. What
  1716. happens is that a new internal node is created for the data object
  1717. (it is created with two external children), and is placed at the
  1718. position occupied by the external node. Think of an internal node
  1719. having no 'room' for a new inserted data object. A sorted tree (for
  1720. example TBinSearchTree below) has enough information to be able to
  1721. restructure the tree (maintaining its essential sorted property) to
  1722. violate this rule if it needs to (but in fact it doesn't).
  1723.  
  1724. For example inserting NewObject at Leaf below:
  1725.  
  1726.        Parent                       Parent
  1727.       /      \         ====>       /      \
  1728.    Subtree    Leaf              Subtree    NewObject
  1729.      |                            |       /         \
  1730.     ...                          ...    Leaf       Leaf
  1731.  
  1732. Rule 2: you can only delete a node that's (a) internal and (b) has at
  1733. least one external child. When you delete the node the external
  1734. child(ren) are automatically deleted as well. The reason for this
  1735. rule is to allow the tree to remain connected after the deletion.
  1736. Again the sorted trees that descend from this object class will know
  1737. how to restructure the tree to allow deletion of an internal node
  1738. with two internal children. There are two cases to consider.  First
  1739. deleting a node with two external children:
  1740.  
  1741.        Parent                       Parent
  1742.       /      \                     /      \
  1743.    Subtree    Node     ====>    Subtree   Leaf
  1744.      |        /  \                |
  1745.     ...     Leaf Leaf            ...
  1746.  
  1747. And second, deleting a node with a single external child (and hence a
  1748. single internal child):
  1749.  
  1750.        Parent                           Parent
  1751.       /      \                         /      \
  1752.   Subtree1    Node        ====>    Subtree1    Subtree2
  1753.     |        /    \                  |            |
  1754.    ...    Leaf   Subtree2           ...          ...
  1755.                     |
  1756.                    ...
  1757.  
  1758. Generally you should only be concerned with this if you are going to
  1759. be creating an unsorted binary tree; to reiterate, the sorted variants
  1760. will restructure the tree using the sorted property to allow insertion
  1761. and deletion at any point.
  1762.  
  1763. Trees can be traversed in many different ways; the methods supported
  1764. by TBinTree are pre order, in order, post order and level order
  1765. traversal, from left to right and vice versa. The definitions of the
  1766. first three of these (from left to right), like that of a binary
  1767. tree, are recursive:
  1768.  
  1769.  PreOrder:  visit the root node, traverse the left sub-tree, traverse
  1770.             the right sub-tree. In the first diagram above, nodes
  1771.             would be visited in the order Root, a, c, d, b, e, f.
  1772.  
  1773.  InOrder:   traverse the left sub-tree, visit the root node, traverse
  1774.             the right sub-tree. In the first diagram above, nodes
  1775.             would be visited in the order c, a, d, Root, e, b, f.
  1776.  
  1777.  PostOrder: traverse the left sub-tree, traverse the right sub-tree,
  1778.             visit the root node. In the first diagram above, nodes
  1779.             would be visited in the order c, d, a, e, f, b, Root.
  1780.  
  1781. The last (LevelOrder) means visiting the root node first, then the
  1782. root node of its left subtree, then the root node of its right
  1783. subtree, then the root node of its left subtree's left subtree, and
  1784. so on, visiting all of the nodes from top to bottom from left to
  1785. right at each level. In the first diagram above, nodes would be
  1786. visited in the order Root, a, b, c, d, e, f.
  1787.  
  1788. When traversing the tree from right to left the definitions are the
  1789. same, except that the visiting order is reversed: you will be
  1790. visiting the right subtree before the left subtree.
  1791.  
  1792.  
  1793. Properties
  1794. ----------
  1795.  
  1796. Declaration
  1797.   property TraversalType : TTraversalType
  1798. Description
  1799.   The traversal type for the Iterate method. Note that you shouldn't
  1800.   alter this property in the middle of a call to the Iterate method
  1801.   (in the Action function for example).
  1802.  
  1803.  
  1804. Interfaced Methods
  1805. ------------------
  1806.  
  1807. Declaration
  1808.   constructor Create(DataOwner : boolean);
  1809. Description
  1810.   Creates the tree by calling the ancestor's Create after setting a
  1811.   node size of 16. The initial traversal order for the Iterate method
  1812.   is set to InOrder.  If DataOwner is true, the new tree will own its
  1813.   data objects.
  1814.  
  1815. Declaration
  1816.   constructor Clone(Source : TAbstractContainer;
  1817.                     DataOwner : boolean; NewCompare : TCompareFunc);
  1818.                                                             override;
  1819. Description
  1820.   Creates a copy of the Source binary tree.
  1821.  
  1822. Declaration
  1823.   function Delete(Cursor : TTreeCursor) : TTreeCursor; virtual;
  1824. Description
  1825.   Unlinks the passed cursor from the tree but does not dispose of its
  1826.   data object.  The cursor of the data object replacing it in the tree
  1827.   is returned. Please note that the parameter Cursor is invalid after
  1828.   this routine is called. In DEBUG mode, an assertion error will occur
  1829.   if Cursor does not have at least one external child, or if Cursor is
  1830.   an external node.
  1831.  
  1832. Declaration
  1833.   procedure Empty; override;
  1834. Description
  1835.   Destroys all nodes and data objects in the tree. Does a post order
  1836.   traversal of the tree calling Erase for all nodes.
  1837.  
  1838. Declaration
  1839.   function Erase(Cursor : TTreeCursor) : TTreeCursor;
  1840. Description
  1841.   Works like Delete except that the data object pointed to by Cursor
  1842.   is disposed of as well, providing that the tree owns its data
  1843.   objects.
  1844.  
  1845. Declaration
  1846.   function Examine(Cursor : TTreeCursor) : pointer;
  1847. Description
  1848.   Returns the data object at Cursor. In DEBUG mode, an assertion error
  1849.   will occur if Cursor is an external node (which have no data objects
  1850.   associated with them).
  1851.  
  1852. Declaration
  1853.   procedure Insert(var Cursor : TTreeCursor; aData : pointer);
  1854.                                                               virtual;
  1855. Description
  1856.   Inserts the data object aData into the tree at Cursor, where Cursor
  1857.   is assumed to be an external node. In DEBUG mode an assertion error
  1858.   will occur if it is not.
  1859.  
  1860. Declaration
  1861.   function IsLeaf(Cursor : TTreeCursor) : boolean;
  1862. Description
  1863.   Returns true if Cursor is pointing at an external node (a leaf).
  1864.   Note that although this implementation of binary trees uses nil for
  1865.   external nodes, this does NOT mean that a cursor that's pointing to
  1866.   an external node is itself nil or zero.
  1867.  
  1868. Declaration
  1869.   function IsRoot(Cursor : TTreeCursor) : boolean;
  1870. Description
  1871.   Returns true if Cursor is pointing at the data object at the tree's
  1872.   root.
  1873.  
  1874. Declaration
  1875.   function Iterate(Action : TIteratorFunc;
  1876.                    Backwards : boolean;
  1877.                    ExtraData : pointer) : TListCursor;
  1878. Description
  1879.   Walks the tree from the root data object in the traversal order
  1880.   defined by TraversalType. If Backwards is False the traversal goes
  1881.   from left to right, if True from right to left. Iterate calls Action
  1882.   for each data object found.  If Action returns False, then this
  1883.   method immediately returns with that data object's cursor; if Action
  1884.   always returns True then this method will return 0. ExtraData is a
  1885.   pointer to any other data that you may want Action to use.
  1886.  
  1887. Declaration
  1888.   procedure Join(Cursor : TTreeCursor; Tree : TBinTree); virtual;
  1889. Description
  1890.   Moves all the data objects in Tree onto the tree at the position
  1891.   pointed to by Cursor, where Cursor is assumed to be an external
  1892.   node. Tree is then disposed of. In DEBUG mode an assertion error
  1893.   will occur if Cursor is not an external node.
  1894.  
  1895. Declaration
  1896.   function Left(Cursor : TTreeCursor) : TTreeCursor;
  1897. Description
  1898.   Returns the cursor of the left child of Cursor. In DEBUG mode, an
  1899.   assertion error will occur if Cursor is at an external node.
  1900.  
  1901. Declaration
  1902.   function Parent(Cursor : TTreeCursor) : TTreeCursor;
  1903. Description
  1904.   Returns the cursor of the parent of Cursor. In DEBUG mode, an
  1905.   assertion error will occur if Cursor is pointing at the root node
  1906.   (which has no parent of course).
  1907.  
  1908. Declaration
  1909.   function Replace(Cursor : TTreeCursor; aData : pointer) : pointer;
  1910. Description
  1911.   Replaces the data object at the cursor with aData and returns the
  1912.   replaced data object. In DEBUG mode, an assertion error will occur
  1913.   if Cursor is an external node.
  1914.  
  1915. Declaration
  1916.   function Right(Cursor : TTreeCursor) : TTreeCursor;
  1917. Description
  1918.   Returns the cursor of the right child of Cursor. In DEBUG mode, an
  1919.   assertion error will occur if Cursor is at an external node.
  1920.  
  1921. Declaration
  1922.   function Root : TTreeCursor;
  1923. Description
  1924.   Returns the cursor of the root data object.
  1925.  
  1926. Declaration
  1927.   function Search(var Cursor : TTreeCursor;
  1928.                       aData  : pointer) : boolean; virtual;
  1929. Description
  1930.   Returns true if the data object aData is found in the tree (the
  1931.   tree's Compare function must return 0 between it and an object in
  1932.   the list), and returns the cursor. If aData was not found then
  1933.   Cursor is undefined.
  1934.  
  1935. ──────────────────────────────────────────────────────────────────────
  1936.  
  1937. TBinSearchTree
  1938. ==============
  1939.  
  1940. A binary search tree is a sorted binary tree where for any given data
  1941. object, all data objects in its left subtree are less than it, and all
  1942. data objects in the right subtree are greater than it. This ordering
  1943. relies on the Compare function to be overridden.
  1944.  
  1945. Binary search trees of this simple type suffer from the well-known
  1946. problem that they can degenerate into sorted linked lists. There is no
  1947. balancing mechanism for dealing with such degenerate cases as
  1948. inserting data objects in sorted order or in other pathological
  1949. orders.
  1950.  
  1951. For example insert the letters abcd into a binary search tree using
  1952. the normal ASCII collating sequence and you'd get the tree:
  1953.  
  1954.                      a
  1955.                       \
  1956.                        b
  1957.                         \
  1958.                          c
  1959.                           \
  1960.                            d
  1961.  
  1962. and if you inserted the letters aebdc into a binary search tree you'd
  1963. get:
  1964.  
  1965.                      a
  1966.                       \
  1967.                        e
  1968.                       /
  1969.                      b
  1970.                       \
  1971.                        d
  1972.                       /
  1973.                      c
  1974.  
  1975. For a balanced tree see the red-black tree TrbSearchTree. However if
  1976. you are fairly sure that either (a) the number of data objects is
  1977. likely to be small and/or (b) they will be inserted in a non-sorted
  1978. fashion, then an ordinary binary search tree will be more efficient.
  1979.  
  1980. Interfaced Methods
  1981. ------------------
  1982.  
  1983. Declaration
  1984.   constructor Clone(Source : TAbstractContainer;
  1985.                     DataOwner : boolean; NewCompare : TCompareFunc);
  1986.                                                              override;
  1987. Description
  1988.   Creates a copy of the Source binary search tree.
  1989.  
  1990. Declaration
  1991.   function Delete(Cursor : TTreeCursor) : TTreeCursor;  virtual;
  1992. Description
  1993.   Deletes the data object at Cursor from the tree, and returns the
  1994.   cursor of the data object that replaced the deleted one. NOTE: you
  1995.   can delete any internal node in a binary search tree (the tree will
  1996.   be rearranged), but Delete will still cause an error if you try and
  1997.   delete an external node.
  1998.  
  1999. Declaration
  2000.   procedure Insert(var Cursor : TTreeCursor; aData : pointer);
  2001.                                                             override;
  2002. Description
  2003.   Searches for the data object with the Search method. If not found,
  2004.   Search will return an external cursor, and the data object is
  2005.   inserted there.  If found an exception will be raised with code
  2006.   edsInsertDup.  The cursor of the newly inserted data object is
  2007.   returned in Cursor.
  2008.  
  2009. Declaration
  2010.   procedure Join(Cursor : TTreeCursor; Tree : TBinTree); override;
  2011. Description
  2012.   Moves all the data objects in Tree into Self. Because of the
  2013.   sorted nature of binary search trees, the new data objects cannot
  2014.   be inserted en masse at Cursor: the tree must maintain its
  2015.   sequence. Hence Cursor is not used and is ignored (but must be
  2016.   defined as Join is a virtual method).
  2017.  
  2018. Declaration
  2019.   function Replace(Cursor : TTreeCursor;
  2020.                     aData : pointer) : pointer;
  2021. Description
  2022.   Replaces the data object of Cursor, and returns the replaced
  2023.   object. Note that this is just shorthand for Delete followed by
  2024.   Insert.
  2025.  
  2026. Declaration
  2027.   function Search (var Cursor : TTreeCursor;
  2028.                          aData : pointer) : boolean; virtual;
  2029. Description
  2030.   Searches for a data object aData by traversing the tree (in 'sorted'
  2031.   order), and calling the Compare method for each visited data object
  2032.   and the given data object aData. If Compare returns 0 (equal),
  2033.   Search will return the cursor of the current data object, if Compare
  2034.   never returns 0, Search returns the cursor of the external node
  2035.   where it ended up (this would be where the data object could be
  2036.   inserted).
  2037.  
  2038. ──────────────────────────────────────────────────────────────────────
  2039.  
  2040. TrbSearchTree
  2041. =============
  2042.  
  2043. A red-black tree is a binary search tree with inbuilt tree balancing
  2044. algorithms during Insert and Delete. This ensures that the tree does
  2045. not degenerate into a sorted linked list, maintaining its excellent
  2046. search times. Balancing in this sense means that the internal
  2047. algorithms try to ensure that the path from every leaf to the root is
  2048. 'about' the same, it does not ensure that they are all equal (or at
  2049. most one apart).
  2050.  
  2051. The tree is called red-black because certain nodes in the tree are
  2052. labelled Black and the others are labelled Red such that
  2053.  
  2054.    (1) all external nodes (or leaves) are Black,
  2055.  
  2056.    (2) every Red data object (that is not at the root) has a Black
  2057.        parent,
  2058.  
  2059.    (3) each path from leaf to root has the same number of Black data
  2060.        objects.
  2061.  
  2062. This set of rules ensures that the tree is (quite) balanced. To see
  2063. that this is so I would recommend you read [Sedgewick] or [Wood].
  2064.  
  2065. Interfaced Methods
  2066. ------------------
  2067.  
  2068. Declaration
  2069.   function Delete(Cursor : TTreeCursor) : TTreeCursor; override;
  2070. Description
  2071.   Deletes the data object at Cursor from the tree, and returns the
  2072.   cursor of the data object that replaced the deleted one. Re-balances
  2073.   the tree if necessary.
  2074.  
  2075. Declaration
  2076.   procedure Insert(var Cursor : TTreeCursor;
  2077.                        aData : pointer); override;
  2078. Description
  2079.   Searches for the data object with Search. If found an exception
  2080.   will be raised with code edsInsertDup.  Otherwise, the data object
  2081.   is inserted at the cursor returned by Search, and the tree is
  2082.   re-balanced if necessary.  The cursor of the newly inserted data
  2083.   object is returned in Cursor.
  2084.  
  2085. ──────────────────────────────────────────────────────────────────────
  2086.  
  2087. TEZCollection
  2088. =============
  2089.  
  2090. A collection is an array of objects. Data objects are referenced by
  2091. a zero-based element number. The collection array expands when
  2092. required to insert new data objects.
  2093.  
  2094. In Borland Pascal 7 (and earlier, Turbo Pascal 6) there was an object
  2095. called TCollection, but this was not transferred over to Delphi;
  2096. instead being replaced by the TList class (and its descendants).
  2097. Unfortunately there was a large group of programmers who were
  2098. migrating their applications from BP7 to Delphi, and therefore had
  2099. need of a TCollection class. They basically had two options, convert
  2100. the TCollection object to a new model TCollection class, or to write
  2101. a TCollection based on the new TList class.
  2102.  
  2103. The TEZCollection provides an array of data objects in the manner of
  2104. TList and TCollection. The array expands automatically when required,
  2105. up to a limit of 1 million objects (cf 16K objects for TList in Delphi
  2106. 1.x and TCollection in BP7).
  2107.  
  2108.  
  2109. Notes for TCollection users
  2110. ---------------------------
  2111.  
  2112. The TEZCollection has been incorporated into the EZDSL container
  2113. heirarchy and so operates is a slightly different way than
  2114. TCollection. The differences are noted below:
  2115.  
  2116.  - TEZCollection has been written to use the Delphi object model,
  2117.    specifically to use classes, exceptions and properties. For
  2118.    example: Count is now a property; the Error method no longer
  2119.    exists, it is replaced by raising specific exceptions.
  2120.  
  2121.  - the Delta field is not required for the algorithm within
  2122.    TEZCollection, it no longer exists.
  2123.  
  2124.  - Limit is now a longint and is a property.
  2125.  
  2126.  - Items is now an array property and the At and AtPut methods are
  2127.    no longer required, since it is easier to code an array index. So
  2128.    replace At(i) and AtPut(i) calls by Items[i]. Note that At and
  2129.    AtPut still exist for compatibility reasons.
  2130.  
  2131.  - the Init constructor has been replaced by the Create constructor.
  2132.    Following the EZDSL standard, this is a virtual constructor taking
  2133.    a single parameter indicating whether the collection is to be a
  2134.    data owner or not. The ALimit and ADelta parameters are not
  2135.    required by TEZCollection.
  2136.  
  2137.  - Load, Store, GetItem, PutItem do not exist at the moment, they
  2138.    will make an appearance in a later version of EZDSL.
  2139.  
  2140.  - the Done destructor is now called Destroy.
  2141.  
  2142.  - FirstThat, LastThat and ForEach no longer exist, they have been
  2143.    replaced by Iterate. Note also that Iterate does NOT use a nested
  2144.    Action routine, it must be global. To replace the calling routine's
  2145.    local data which is used by the nested Action routine, use the
  2146.    ExtraData pointer.
  2147.  
  2148.  - Pack does not delete all the nil pointers in the collection; it
  2149.    squeezes unused space from the internal array fields instead.
  2150.  
  2151.  - the FreeItem method has been replaced by the DisposeData property,
  2152.    like all other EZDSL classes.
  2153.  
  2154.  
  2155. Properties
  2156. ----------
  2157.  
  2158. Declaration
  2159.   property Items[Index : longint] : pointer
  2160. Description
  2161.   An array property: the array of items in the collection. Index is
  2162.   in the range zero to Count-1, outside of this range an exception
  2163.   will be generated. Items is a default array property as well: you
  2164.   can use the collection as if it were an array without the need to
  2165.   reference the Items property explicitly.
  2166.  
  2167. Declaration
  2168.   property Limit : longint
  2169. Description
  2170.   READ ONLY. The maximum number of items that the collection can hold
  2171.   at the moment without further expansion.
  2172.  
  2173.  
  2174. Interfaced methods
  2175. ------------------
  2176.  
  2177. Declaration
  2178.   constructor Create(DataOwner : boolean); override;
  2179. Description
  2180.   Creates the collection by calling the ancestor's Create after
  2181.   setting a node size of 0 (ie the collection is responsible for its
  2182.   'nodes'). If DataOwner is true, the new tree will own its data
  2183.   objects.
  2184.  
  2185. Declaration
  2186.   constructor Clone(Source : TAbstractContainer;
  2187.                     DataOwner : boolean; NewCompare : TCompareFunc);
  2188.                                                              override;
  2189. Description
  2190.   Creates a copy of the Source collection.
  2191.  
  2192. Declaration
  2193.   destructor Destroy; override;
  2194. Description
  2195.   Calls Empty, and then destroys the internal structures.
  2196.  
  2197. Declaration
  2198.   procedure Assign(Source : TPersistent); override;
  2199. Description
  2200.   If the Source object is a TEZCollection, empties itself with Empty,
  2201.   sets the DisposeData, DupData and CompareData properties equal to
  2202.   Source's, and then copies all of Source's data objects into itself
  2203.   (the data will be duplicated if Source is a data owner).
  2204.  
  2205. Declaration
  2206.   function  At(Index : longint) : pointer;
  2207. Description
  2208.   Returns the data object at Index in the collection. An exception is
  2209.   raised if Index is not in the range 0 to Count-1.
  2210.  
  2211. Declaration
  2212.   procedure AtDelete(Index : longint);
  2213. Description
  2214.   Deletes the item at Index in the collection, items below are moved
  2215.   up one position.  The data object is not disposed of. An exception
  2216.   is raised if Index is not in the range 0 to Count-1.
  2217.  
  2218. Declaration
  2219.   procedure AtFree(Index : longint);
  2220. Description
  2221.   Like AtDelete, but also disposes of the data object (providing the
  2222.   collection is a data owner).
  2223.  
  2224. Declaration
  2225.   procedure AtInsert(Index : longint; Item : pointer);
  2226. Description
  2227.   Inserts the data object at Index in the collection. The items at
  2228.   Index and below are moved down one position. An exception is raised
  2229.   if Index is not in the range 0 to Count (AtInsert when called with
  2230.   an Index of Count will append the item to the end of the array).
  2231.   Another exception will be raised if the collection has the maximum
  2232.   number of items already.
  2233.  
  2234. Declaration
  2235.   procedure AtPut(Index : longint; Item : pointer);
  2236. Description
  2237.   Replaces the data object at Index in the collection with the new
  2238.   data object Item.  An exception is raised if Index is not in the
  2239.   range 0 to Count-1.
  2240.  
  2241. Declaration
  2242.   procedure Delete(Item : pointer);
  2243. Description
  2244.   Deletes the data object given by Item from the collection. The data
  2245.   object is not disposed of. The routine is coded as a call to
  2246.   IndexOf, followed by a call to AtDelete providing the data object
  2247.   was found.
  2248.  
  2249. Declaration
  2250.   procedure DeleteAll;
  2251. Description
  2252.   Deletes all data objects from the collection; none are disposed of.
  2253.   After a call to DeleteAll the number of data objects in the
  2254.   collection will be zero.
  2255.  
  2256. Declaration
  2257.   procedure Free(Item : pointer);
  2258. Description
  2259.   Like Delete except that the data object will be disposed of,
  2260.   provided that the collection is a data owner. NOTE: this method will
  2261.   replace TObject.Free (the method that checks if Self is nil and if
  2262.   not calls Destroy); to call the original version, code it as
  2263.   TObject(MyCollection).Free.
  2264.  
  2265. Declaration
  2266.   procedure FreeAll;
  2267. Description
  2268.   Calls Empty to dispose of and delete all data objects in the
  2269.   collection.
  2270.  
  2271. Declaration
  2272.   function  IndexOf(Item : pointer) : longint; virtual;
  2273. Description
  2274.   Returns the index of the Item data object. If not found the routine
  2275.   returns -1. In this class, this is coded as a sequential search
  2276.   through the collection looking for a data object with the same
  2277.   pointer value as Item; sorted descendants will use a binary search
  2278.   technique using the Compare property.
  2279.  
  2280. Declaration
  2281.   procedure Insert(Item : pointer); virtual;
  2282. Description
  2283.   Inserts the data object into the collection. In this class the data
  2284.   object is appended to the end of the collection; sorted descendants
  2285.   will insert the data object in the correct sequence.
  2286.  
  2287. Declaration
  2288.   function  Iterate(Action : TIterator; Backwards : boolean;
  2289.                     ExtraData : pointer) : pointer;
  2290. Description
  2291.   Iterate through all the data objects in the collection calling
  2292.   Action for each one. If Backwards is False the iteration proceeds
  2293.   from the 0 to (Count-1), if True the iteration proceeds from
  2294.   (Count-1) to 0. If Action returns False for a data object, Iterate
  2295.   terminates immediately with the data object as the function result.
  2296.   If Action always returns True, Iterate will return nil.
  2297.  
  2298. Declaration
  2299.   procedure Pack;
  2300. Description
  2301.   Removes all unused space from the internal structures in the
  2302.   collection. If a collection's data remains fairly static (at least
  2303.   in terms of numbers) it might be beneficial to call Pack to reduce
  2304.   the overall amount of space used by the collection.
  2305.  
  2306. ──────────────────────────────────────────────────────────────────────
  2307.  
  2308. TEZSortedCollection
  2309. ===================
  2310.  
  2311. A sorted variant of TEZCollection. As it is sorted you must provide a
  2312. Compare routine.
  2313.  
  2314.  
  2315. Interfaced methods
  2316. ------------------
  2317.  
  2318. Declaration
  2319.   function  IndexOf(Item : pointer) : longint; override;
  2320. Description
  2321.   Returns the index of an item: the item is searched for by using the
  2322.   fact that the collection is sorted.
  2323.  
  2324. Declaration
  2325.   procedure Insert(Item : pointer); override;
  2326. Description
  2327.   Inserts the item into the collection into the proper point in the
  2328.   sorted sequence as defined by the Compare routine. If the item is
  2329.   already present, an exception is raised with code edsInsertDup.
  2330.  
  2331. Declaration
  2332.   function  Search(Item : pointer; var Index : longint) : boolean;
  2333.                                                               virtual;
  2334. Description
  2335.   Searches for the item in the collection by using the fact that the
  2336.   collection is sorted. If found it returns true and the index of the
  2337.   item in the collection. If not found it will return false and Index
  2338.   is the index where the item would be inserted.
  2339.  
  2340. ──────────────────────────────────────────────────────────────────────
  2341.  
  2342. TEZStringCollection
  2343. ===================
  2344.  
  2345. A variant of TEZSortedCollection where the items are all strings
  2346. (short strings in Delphi 2.0). The Create constructor will set up the
  2347. DupData, Compare and DisposeData routines for you: the container can
  2348. be used as is.
  2349.  
  2350.  
  2351. Interfaced methods
  2352. ------------------
  2353.  
  2354. Declaration
  2355.   constructor TEZStringCollection.Create(DataOwner : boolean);
  2356. Description
  2357.   Calls the ancestor's Create constructor and then sets the DupData
  2358.   property to EZStrDupData, the DisposeData property to
  2359.   EZStrDisposeData and the Compare property to EZStrCompare.
  2360.  
  2361. ──────────────────────────────────────────────────────────────────────
  2362.  
  2363. TEZStrZCollection
  2364. ===================
  2365.  
  2366. A variant of TEZSortedCollection where the items are all null-
  2367. terminated strings (aka PChars). The Create constructor will set up
  2368. the DupData, Compare and DisposeData routines for you: the container
  2369. can be used as is.
  2370.  
  2371.  
  2372. Interfaced methods
  2373. ------------------
  2374.  
  2375. Declaration
  2376.   constructor TEZStrZCollection.Create(DataOwner : boolean);
  2377. Description
  2378.   Calls the ancestor's Create constructor and then sets the DupData
  2379.   property to EZStrZDupData, the DisposeData property to
  2380.   EZStrZDisposeData and the Compare property to EZStrZCompare.
  2381.  
  2382. ──────────────────────────────────────────────────────────────────────
  2383.  
  2384.  
  2385. And Finally...
  2386. ----------------------------------------------------------------------
  2387.  
  2388. ..the legal bit.
  2389.  
  2390. I am releasing the EZDSL library and accompanying example programs,
  2391. units, source code and documentation as freeware. To remind you,
  2392. freeware means that it doesn't cost you anything, but that I retain
  2393. full and all copyright. You may include EZDSL in compiled executable
  2394. programs or DLLs with no royalty charges being levied, but you may
  2395. not distribute for monetary gain any of the source code,
  2396. documentation, example programs, or compiled units within EZDSL with
  2397. source of your own (as a programming library for example) without
  2398. first asking me and getting my approval, and without including my
  2399. copyright notice and without paying money to the charity of my choice
  2400. for the pleasure of doing so.
  2401.  
  2402. I also do not assume any liability whatsoever for your use or misuse
  2403. of the EZDSL unit, source code, documentation and example programs.
  2404. This code (as is ALL code) is liable to have bugs: if it's important
  2405. to you, test, test and then test again.
  2406.  
  2407. If you want the latest version of EZDSL you can get it officially
  2408. from two places: the TurboPower BBS (on 719 260 9726, any baud, 8N1)
  2409. and from the DELPHI forum on CompuServe. I am not interested in
  2410. supporting any other outlets, excellent though they may be.
  2411.  
  2412. Enjoy. If you have any problems, you can get in touch with me via
  2413. private e-mail on CompuServe on [100116,1572]. For Internet users
  2414. that's 100116.1572@compuserve.com.  Similarly, if you'd like some
  2415. extensions to it get in touch and I'll see what I can do.
  2416.  
  2417.  
  2418. Dedication
  2419. ----------------------------------------------------------------------
  2420. To Joanna, I'm deeply sorry it didn't work out.
  2421.  
  2422.  
  2423.                  Julian M. Bucknall, Colorado Springs, USA, March 1996
  2424.  
  2425. EZDSL, the library, the units, the include files and this
  2426. documentation, is Copyright (c) 1993-1996 Julian M. Bucknall
  2427. ======================================================================
  2428.